package rbb.itm5.bwinf.roboter.Control;

import java.awt.Point;
import java.util.ArrayList;
import rbb.itm5.bwinf.roboter.Model.Door;
import rbb.itm5.bwinf.roboter.Model.Obstacle;
import rbb.itm5.bwinf.roboter.Model.RoboterInterface;
import rbb.itm5.bwinf.roboter.Model.Room;
import rbb.itm5.bwinf.roboter.Model.Step;

/**
 * Diese Klasse steuert den Roboter durch den Raum und prüft auf Hindernisse.
 * Der Roboter hat keine visuellen Sensoren jedoch ein Kontaktsensor je
 * Blickrichtung. Außerdem kann er sich nur um 90 Grad drehen. Jeder Schritt
 * bzw. jede Drehung wird gespeichert um eine Zeichnung des Weges zu erstellen.
 *
 * @author Dominik Jakielski
 *
 */
public class Roboter extends RoboterInterface {

    private Room room;
    private ControlerEvents event;

    /**
     * Im Konstruktor wird der Roboter auf seine relative Startposition von
     * (X=0,Y=0) gesetzt. Außerdem werden die Events zum neu zeichnen der View
     * registriert.
     *
     * @param room
     * @param event EventHandler der die SteuerEvents des Roboter entgegen
     * nimmt.
     */
    public Roboter(Room room, ControlerEvents event) {
        this.room = room;
        this.event = event;
        x = 0;
        y = 0;
        rememberStep();
    }

    boolean hasColision(int step) {
        return hasColision(step, getDirection());
    }

    /**
     * Prüft, ob sich Links vom Roboter ein Hindernis bzw. eine Wand befindet
     */
    public boolean hasLeftColision() {
        return hasColision(FORWARD, getDirectionLeft());
    }

    /**
     * Prüft, ob sich Rechts vom Roboter ein Hindernis bzw. eine Wand befindet
     */
    public boolean hasRightColision() {
        return hasColision(FORWARD, getDirectionRight());
    }

    /**
     * Prüft, ob sich der Roboter vor einem Hindernis bzw. einer Wand befindet
     */
    public boolean hasColision() {
        return hasColision(FORWARD, getDirection());
    }

    @Override
    protected boolean hasColision(int step, int direction) {
        ArrayList<Obstacle> obstacles = room.getObstacles();
        boolean colision = false;
        //Relative Koordinaten in absolute Koordinaten umrechnen. 
        x += room.getRoboterStartX();
        y += room.getRoboterStartY();
        Door door = room.getDoor();
        //System.out.println("--------------------------");
        for (Obstacle obstacle : obstacles) {
            boolean c1 = false, c2 = false, c3 = false, c4 = false;
            int oX = obstacle.getPostion().x;
            int oY = obstacle.getPostion().y;
            int oW = obstacle.getSize().width - 1;
            int oH = obstacle.getSize().height - 1;
            switch (direction) {
                case DIRECTION_UP:
                    //System.out.println(x+","+(y-step)+" - "+oX+","+oY+" - "+oW+","+oH);
                    if (y - step >= oY && y - step <= oY + oH && x >= oX && x <= oX + oW) {
                        c1 = true;
                    }
                    //System.out.println((y*Room.GridSize)+" - "+(view.getRoom().getOffset())+" = "+(y*Room.GridSize<=view.getRoom().getOffset()));
                    //System.out.println(door.getOrientation()==Door.HORIZONTAL && x>=door.getPostion().x && x<=door.getPostion().x+door.getSize() && door.getPostion().y<=1);
                    if (y <= 1 && !(door.getOrientation() == Door.HORIZONTAL && x >= door.getPostion().x && x < door.getPostion().x + door.getSize() && door.getPostion().y <= 1)) {
                        c1 = true;
                    }
                    break;
                case DIRECTION_RIGHT:
                    //System.out.println((x+step)+","+(y)+" - "+oX+","+oY+" - "+oW+","+oH);
                    if (x + step >= oX && x + step <= oX + oW && y >= oY && y <= oY + oH) {
                        c2 = true;
                    }
                    //System.out.println((x*Room.GridSize+Room.GridSize)+" - "+(view.getRoom().getSize().width-view.getRoom().getOffset()*2-Room.GridSize));
                    if (x + step >= room.getSize().width - 1
                            && !(door.getOrientation() == Door.VERTICAL && y >= door.getPostion().y && y < door.getPostion().y + door.getSize() && door.getPostion().x >= room.getSize().width - 1)) {
                        c2 = true;
                    }
                    break;
                case DIRECTION_DOWN:
                    //System.out.println((x)+","+(y+step)+" - "+oX+","+oY+" - "+oW+","+oH);
                    if (y + step >= oY && y + step <= oY + oH && x >= oX && x <= oX + oW) {
                        c3 = true;
                    }
                    //System.out.println((y*Room.GridSize+(Room.GridSize*2))+" - "+(view.getRoom().getSize().height-view.getRoom().getOffset()-Room.GridSize));

                    if (y + step >= room.getSize().height - 1
                            && !(door.getOrientation() == Door.HORIZONTAL && x >= door.getPostion().x && x < door.getPostion().x + door.getSize() && door.getPostion().y >= room.getSize().height - 1)) {
                        c3 = true;
                    }
                    break;
                case DIRECTION_LEFT:
                    //System.out.println((x-step)+","+(y)+" - "+oX+","+oY+" - "+oW+","+oH);
                    if (x - step >= oX && x - step <= oX + oW && y >= oY && y <= oY + oH) {
                        c4 = true;
                    }
                    if (x <= 1
                            && !(door.getOrientation() == Door.VERTICAL && y >= door.getPostion().y && y < door.getPostion().y + door.getSize() && door.getPostion().x <= 1)) {
                        c4 = true;
                    }
                    break;

            }
            //Hat eine Kolision?
            if (c1 || c2 || c3 || c4) {
                //System.out.println("hasObstacle: "+c1+","+c2+","+c3+","+c4);
                colision = true;
                break;
            }
        }
        //Absolute Koordinaten in relative Koordinaten umrechnen.
        x -= room.getRoboterStartX();
        y -= room.getRoboterStartY();
        return colision;
    }

    @Override
    public boolean goForward() throws InterruptedException {
        if (!hasColision()) {
            goStep(FORWARD);
            event.roboterPositionChanged();
            return true;
        }
        return false;
    }

    @Override
    public void turnLeft() {
        super.turnLeft();
        //System.out.println("turnLeft: "+DIRECTION[getDirection()]);
        rememberStep(true);
        event.repaintView();
    }

    @Override
    public void turnRight() {
        super.turnRight();
        //System.out.println("turnRight: "+DIRECTION[getDirection()]);
        rememberStep(true);
        event.repaintView();
    }

    @Override
    public boolean moveForward() {
        if (!hasColision()) {
            moveStep(FORWARD);
            return true;
        }
        return false;
    }

    @Override
    public void goBack() {
        if (!steps.isEmpty()) {
            Step s = steps.get(steps.size() - 1);
            steps.remove(s);
            int direction = s.getDirection() - getDirection();
            if (direction > 0) {
                super.turnRight();
            }
            if (direction < 0) {
                super.turnLeft();
            }
            x = s.getX();
            y = s.getY();
            event.repaintView();
        }
    }

    /**
     * Berechnet die Positionen des nächsten Schritts.
     * @param step Anzahl der Schritte.
     */
    private void calcStep(int step) {
        switch (getDirection()) {
            case DIRECTION_LEFT:
                x -= step;
                break;
            case DIRECTION_RIGHT:
                x += step;
                break;
            case DIRECTION_UP:
                y -= step;
                break;
            case DIRECTION_DOWN:
                y += step;
                break;
        }
    }

    /**
     * Bewegt den Roboter nach vorne und speichert die zurückgelegten Schritte..
     * @param step Anzahl der Schritte
     */
    private void goStep(int step) {
        rememberStep();
        calcStep(step);
        event.repaintView();
    }

    private void moveStep(int step) {
        rememberStep();
        calcStep(step);
    }

    ControlerEvents getEvent() {
        return event;
    }
    
    @Override
    public boolean checkGoalHasFound() {
        goolHasFound = goolHasFound
                || x + room.getRoboterStartX() < 1
                || y + room.getRoboterStartY() < 1
                || x + room.getRoboterStartX() > room.getSize().width - 2
                || y + room.getRoboterStartY() > room.getSize().height - 2;
        return goolHasFound;
    }

    /**
     * Setzt alle Schritte und die Position des Roboters zurück.
     */
    void reset() {
        super.resetValues();
    }

    /**
     * Gibt alle gegangenen Schritte zurück.
     * @see ArrayList
     * @return Schritt Speicher.
     */
    public ArrayList<Step> getSteps() {
        if (!(steps instanceof ArrayList)) {
            return null;
        }
        return (ArrayList<Step>) steps.clone();
    }

    /**
     * Gibt die nächste Position in Blickrichtung aus.
     * @see Point
     * @return nächste Position als Point
     */
    public Point getNextPosition() {
        return getNextPosition(getDirection(), 1);
    }

    /**
     * Gibt die nächste Position in genannter Richtung aus.
     * @see Point
     * @return nächste Position als Point
     */
    public Point getNextPosition(int direction) {
        return getNextPosition(direction, 1);
    }

    /**
     * Gibt die Position in x Schritten und in genannter Richtung aus.
     * @see Point
     * @return Position als Point
     */
    public Point getNextPosition(int direction, int steps) {
        int Mx = x, My = y, dir = getDirection();
        setDirection(direction);
        calcStep(FORWARD * steps);
        Point p = new Point(x, y);
        setDirection(dir);
        x = Mx;
        y = My;
        return p;
    }
}
